/*
 * Copyright (c) 2009, Michael van der Westhuizen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *    - Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    - Redistributions in binary form must reproduce the above
 *      copyright notice, this list of conditions and the following
 *      disclaimer in the documentation and/or other materials provided
 *      with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef MQMESSAGE_MCVDW_20090517_C
#define MQMESSAGE_MCVDW_20090517_C

#include "imq.h"

#include "mqstatus.c"
#include "util.c"
#include "mqdestination.c"

/* *****************************************************************************
 * *****************************************************************************
 * *****************************************************************************
 * MQMessage
 * *****************************************************************************
 * *****************************************************************************
 * ****************************************************************************/


static int mqcrt_MQMessage_traverse(mqcrt_MQMessage *self, visitproc visit, void *arg)
{
    DPRINTF(("%s\n", "INFO: mqcrt_MQMessage_traverse: entry"));
    Py_VISIT(self->session);
    DPRINTF(("%s\n", "INFO: mqcrt_MQMessage_traverse: exit"));
    return 0;
}


static int mqcrt_MQMessage_clear(mqcrt_MQMessage *self)
{
    DPRINTF(("%s\n", "INFO: mqcrt_MQMessage_clear: entry"));
    Py_CLEAR(self->session);
    DPRINTF(("%s\n", "INFO: mqcrt_MQMessage_clear: exit"));
    return 0;
}


static void mqcrt_MQMessage_dealloc(mqcrt_MQMessage *self)
{
    DPRINTF(("%s\n", "INFO: mqcrt_MQMessage_dealloc: entry"));
    SAFE_FREE_MESSAGE(self->message);
    mqcrt_MQMessage_clear(self);
    self->ob_type->tp_free((PyObject*)self);
    DPRINTF(("%s\n", "INFO: mqcrt_MQMessage_dealloc: exit"));
}


static PyObject * mqcrt_MQMessage_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    mqcrt_MQMessage *self;
    DPRINTF(("%s\n", "INFO: mqcrt_MQMessage_new: entry"));

    self = (mqcrt_MQMessage *)type->tp_alloc(type, 0);
    INIT_HANDLE(self->message);
    self->session = NULL;

    DPRINTF(("%s\n", "INFO: mqcrt_MQMessage_new: exit"));
    return (PyObject *)self;
}


static int mqcrt_MQMessage_init(mqcrt_MQMessage *self, PyObject *args, PyObject *kwds)
{
    MQMessageType type;
	MQMessageHandle handle;

    static char *kwlist[] = {"type", NULL};

    DPRINTF(("%s\n", "INFO: mqcrt_MQMessage_init: entry"));

    type = MQ_UNSUPPORTED_MESSAGE;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &type)) {
        return -1;
    }

    COPY_HANDLE(handle, self->message);
    INIT_HANDLE(self->message);

    if (MQ_TEXT_MESSAGE == type) {
        if (setExceptionFromStatus(MQCreateTextMessage(&self->message))) {
            return -1;
        }
    } else if (MQ_BYTES_MESSAGE == type) {
        if (setExceptionFromStatus(MQCreateBytesMessage(&self->message))) {
            return -1;
        }
    } else if (MQ_UNSUPPORTED_MESSAGE == type) {
        /* nada */
    } else if (MQ_MESSAGE == type) {
        /* this is largely useless - should we even allow it? */
        if (setExceptionFromStatus(MQCreateMessage(&self->message))) {
            return -1;
        }
    } else {
        COPY_HANDLE(self->message, handle);
        PyErr_Format(PyExc_ValueError, "%d is not a supported message type", type);
        return -1;
    }

    SAFE_FREE_MESSAGE(handle);
    Py_XDECREF(self->session);
    self->session = NULL;
    DPRINTF(("%s\n", "INFO: mqcrt_MQMessage_init: exit"));
    return 0;
}


static PyObject * mqcrt_MQMessage_type(mqcrt_MQMessage *self, PyObject *args, PyObject *kwds)
{
    MQMessageType type;

    if (setExceptionFromStatus(MQGetMessageType(self->message, &type))) {
        return NULL;
    }

    return Py_BuildValue("i", type);
}


static PyObject * mqcrt_MQMessage_get_properties(mqcrt_MQMessage *self, PyObject *args, PyObject *kwds)
{
    MQPropertiesHandle properties;
    MQStatus status;
    PyObject * dict;

    INIT_HANDLE(properties);

    status = MQGetMessageProperties(self->message, &properties);

    if (MQ_NO_MESSAGE_PROPERTIES == MQGetStatusCode(status)) {
        return PyDict_New();
    }

    if (setExceptionFromStatus(status)) {
        return NULL;
    }

    if (NULL == (dict = PyDict_New())) {
        MQFreeProperties(properties);
        return NULL;
    }

    if (0 != propertiesToDict(dict, properties)) {
        MQFreeProperties(properties);
        Py_CLEAR(dict);
        return NULL;
    }

    MQFreeProperties(properties);
    return dict;
}


static PyObject * mqcrt_MQMessage_set_properties(mqcrt_MQMessage *self, PyObject *args, PyObject *kwds)
{
    MQPropertiesHandle properties;
    PyObject * props_dict;

    static char *kwlist[] = {"properties", NULL};

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &props_dict)) {
        return NULL;
    }

    INIT_HANDLE(properties);

    if (0 != dictToProperties(&properties, props_dict)) {
        return NULL;
    }

    if (setExceptionFromStatus(MQSetMessageProperties(self->message, properties))) {
        return NULL;
    }

    INIT_HANDLE(properties);
    Py_RETURN_NONE;
}


static PyObject * mqcrt_MQMessage_get_headers(mqcrt_MQMessage *self, PyObject *args, PyObject *kwds)
{
    MQPropertiesHandle headers;
    PyObject * dict;

    INIT_HANDLE(headers);

    if (setExceptionFromStatus(MQGetMessageHeaders(self->message, &headers))) {
        return NULL;
    }

    if (NULL == (dict = PyDict_New())) {
        MQFreeProperties(headers);
        return NULL;
    }

    if (0 != propertiesToDict(dict, headers)) {
        MQFreeProperties(headers);
        Py_CLEAR(dict);
        return NULL;
    }

    MQFreeProperties(headers);
    return dict;
}


static PyObject * mqcrt_MQMessage_set_headers(mqcrt_MQMessage *self, PyObject *args, PyObject *kwds)
{
    MQPropertiesHandle properties;
    PyObject * props_dict;

    static char *kwlist[] = {"headers", NULL};

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &props_dict)) {
        return NULL;
    }

    INIT_HANDLE(properties);

    if (0 != dictToProperties(&properties, props_dict)) {
        return NULL;
    }

    if (setExceptionFromStatus(MQSetMessageHeaders(self->message, properties))) {
        return NULL;
    }

    INIT_HANDLE(properties);
    Py_RETURN_NONE;
}


static PyObject * mqcrt_MQMessage_get_reply_to(mqcrt_MQMessage *self, PyObject *args, PyObject *kwds)
{
    MQDestinationHandle handle;
    PyObject * obj;
    MQStatus status;

    INIT_HANDLE(handle);

    status = MQGetMessageReplyTo(self->message, &handle);

    if (MQGetStatusCode(status) == MQ_NO_REPLY_TO_DESTINATION) {
        Py_RETURN_NONE;
    }

    if (setExceptionFromStatus(status)) {
        return NULL;
    }

    if (NULL == (obj = mqcrt_MQDestination_Factory(handle, NULL))) {
        MQFreeDestination(handle);
        return NULL;
    }

    return obj;
}


static PyObject * mqcrt_MQMessage_set_reply_to(mqcrt_MQMessage *self, PyObject *args, PyObject *kwds)
{
    mqcrt_MQDestination * dst;

    static char *kwlist[] = {"destination", NULL};

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &dst)) {
        return NULL;
    }

    if (setExceptionFromStatus(MQSetMessageReplyTo(self->message, dst->destination))) {
        return NULL;
    }

    Py_RETURN_NONE;
}


static PyObject * mqcrt_MQMessage_acknowledge_messages(mqcrt_MQMessage *self, PyObject *args, PyObject *kwds)
{
    if (setExceptionFromStatus(MQAcknowledgeMessages(((mqcrt_MQSession *)(self->session))->session, self->message))) {
        return NULL;
    }

    Py_RETURN_NONE;
}


static PyObject * mqcrt_MQMessage_get_bytes(mqcrt_MQMessage *self, PyObject *args, PyObject *kwds)
{
    const MQInt8 * messageBytes;
    MQInt32 messageBytesSize;
#if PY_VERSION_HEX < 0x02060000
    PyObject * obj;
    void * memptr;
    Py_ssize_t memlen;
#endif

    if (setExceptionFromStatus(MQGetBytesMessageBytes(self->message, &messageBytes, &messageBytesSize))) {
        return NULL;
    }

#if PY_VERSION_HEX < 0x02060000
    if (NULL == (obj = PyBuffer_New(messageBytesSize))) {
        return NULL;
    }

    if (!PyObject_AsWriteBuffer(obj, &memptr, &memlen)) {
        Py_DECREF(obj);
        return NULL;
    }

    if (messageBytesSize != memlen) {
        PyErr_SetString(PyExc_RuntimeError, "messageBytesSize != memlen");
        Py_DECREF(obj);
        return NULL;
    }

    memcpy(memptr, (const void *)messageBytes, memlen);
    return obj;
#else
    return PyByteArray_FromStringAndSize(messageBytes, messageBytesSize);
#endif
}


static PyObject * mqcrt_MQMessage_set_bytes(mqcrt_MQMessage *self, PyObject *args, PyObject *kwds)
{
    PyObject * obj;
#if PY_VERSION_HEX < 0x02060000
    const void * buf;
    Py_ssize_t buf_len;
#endif

    static char *kwlist[] = {"bytes", NULL};

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &obj)) {
        return NULL;
    }

    if (obj == Py_None) {
        if (setExceptionFromStatus(MQSetBytesMessageBytes(self->message, (const MQInt8 *)"", 0))) {
            return NULL;
        }

        Py_RETURN_NONE;
    }

#if PY_VERSION_HEX < 0x02060000
    if (!PyObject_AsReadBuffer(obj, &buf, &buf_len)) {
        return NULL;
    }

    if (setExceptionFromStatus(MQSetBytesMessageBytes(self->message, (const MQInt8 *)buf, (MQInt32)buf_len))) {
        return NULL;
    }
#else
    /* PyStringArray_Check */
    /* should we be using a view onto a buffer? probably yes */
    if (setExceptionFromStatus(MQSetBytesMessageBytes(self->message, PyByteArray_AS_STRING(obj), PyByteArray_GET_SIZE(obj)))) {
        return NULL;
    }
#endif

    Py_RETURN_NONE;
}


static PyObject * mqcrt_MQMessage_get_text(mqcrt_MQMessage *self, PyObject *args, PyObject *kwds)
{
    ConstMQString messageText;

    if (setExceptionFromStatus(MQGetTextMessageText(self->message, &messageText))) {
        return NULL;
    }

    return PyUnicode_DecodeUTF8(messageText, strlen(messageText), "strict");
}


static PyObject * mqcrt_MQMessage_set_text(mqcrt_MQMessage *self, PyObject *args, PyObject *kwds)
{
    PyObject * text;
    static char *kwlist[] = {"text", NULL};

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &text)) {
        return NULL;
    }

    if (text == Py_None) {
        if (setExceptionFromStatus(MQSetTextMessageText(self->message, ""))) {
            return NULL;
        }

        Py_RETURN_NONE;
    }

    printf("%s\n", "XXX: calling coerceObjectToUtf8String");
    if (NULL == (text = coerceObjectToUtf8String(text))) {
        printf("%s\n", "XXX: coerceObjectToUtf8String FAILED");
        return NULL;
    }
    printf("%s\n", "XXX: coerceObjectToUtf8String was cool");

    printf("%s\n", "XXX: calling PyString_AsString");
    if (setExceptionFromStatus(MQSetTextMessageText(self->message, PyString_AsString(text)))) {
        printf("%s\n", "XXX: PyString_AsString FAILED");
        Py_XDECREF(text);
        return NULL;
    }
    printf("%s\n", "XXX: PyString_AsString was cool");

    Py_XDECREF(text);
    Py_RETURN_NONE;
}


static PyMethodDef mqcrt_MQMessage_methods[] = {
    {"type", (PyCFunction)mqcrt_MQMessage_type, METH_NOARGS, "Returns the type of the message."},
    {"get_properties", (PyCFunction)mqcrt_MQMessage_get_properties, METH_NOARGS, "Returns the properties of the message."},
    {"set_properties", (PyCFunction)mqcrt_MQMessage_set_properties, METH_VARARGS | METH_KEYWORDS, "Sets the properties of the message. The properties object is invalid after this call."},
    {"get_headers", (PyCFunction)mqcrt_MQMessage_get_headers, METH_NOARGS, "Returns the headers of the message."},
    {"set_headers", (PyCFunction)mqcrt_MQMessage_set_headers, METH_VARARGS | METH_KEYWORDS, "Sets the headers of the message. The properties object is invalid after this call."},
    {"get_reply_to", (PyCFunction)mqcrt_MQMessage_get_reply_to, METH_NOARGS, "Gets the reply to destination for this message."},
    {"set_reply_to", (PyCFunction)mqcrt_MQMessage_set_reply_to, METH_VARARGS | METH_KEYWORDS, "Sets the reply to destination for this message."},
    {"acknowledge_messages", (PyCFunction)mqcrt_MQMessage_acknowledge_messages, METH_NOARGS, "Acknowledges the message and all other messages that were received before it on the same session."},
    {"get_bytes", (PyCFunction)mqcrt_MQMessage_get_bytes, METH_NOARGS, "Gets the bytes from a bytes message. Do not call this if the message is not a bytes message."},
    {"set_bytes", (PyCFunction)mqcrt_MQMessage_set_bytes, METH_VARARGS | METH_KEYWORDS, "Sets the bytes for a bytes message. Do not call this if the message is not a bytes message."},
    {"get_text", (PyCFunction)mqcrt_MQMessage_get_text, METH_NOARGS, "Gets the text from a text message. Do not call this if the message is not a text message."},
    {"set_text", (PyCFunction)mqcrt_MQMessage_set_text, METH_VARARGS | METH_KEYWORDS, "Sets the text for a text message. Do not call this if the message is not a text message."},
    {NULL}
};


static PyTypeObject mqcrt_MQMessageType = {
    PyObject_HEAD_INIT(NULL)
    0,                                       /* ob_size */
    "imq.MQMessage",                         /* tp_name */
    sizeof(mqcrt_MQMessage),                 /* tp_basicsize */
    0,                                       /* tp_itemsize */
    (destructor)mqcrt_MQMessage_dealloc,     /* tp_dealloc */
    0,                                       /* tp_print */
    0,                                       /* tp_getattr */
    0,                                       /* tp_setattr */
    0,                                       /* tp_compare */
    0,                                       /* tp_repr */
    0,                                       /* tp_as_number */
    0,                                       /* tp_as_sequence */
    0,                                       /* tp_as_mapping */
    0,                                       /* tp_hash */
    0,                                       /* tp_call */
    0,                                       /* tp_str */
    0,                                       /* tp_getattro */
    0,                                       /* tp_setattro */
    0,                                       /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */
    "MQMessage objects",                     /* tp_doc */
    (traverseproc)mqcrt_MQMessage_traverse,  /* tp_traverse */
    (inquiry)mqcrt_MQMessage_clear,          /* tp_clear */
    0,                                       /* tp_richcompare */
    0,                                       /* tp_weaklistoffset */
    0,                                       /* tp_iter */
    0,                                       /* tp_iternext */
    mqcrt_MQMessage_methods,                 /* tp_methods */
    0,                                       /* tp_members */
    0,                                       /* tp_getset */
    0,                                       /* tp_base */
    0,                                       /* tp_dict */
    0,                                       /* tp_descr_get */
    0,                                       /* tp_descr_set */
    0,                                       /* tp_dictoffset */
    (initproc)mqcrt_MQMessage_init,          /* tp_init */
    0,                                       /* tp_alloc */
    mqcrt_MQMessage_new,                     /* tp_new */
    0,
};

#endif
